package edu.midir.demo.core;

import edu.midir.demo.pojo.ResultBean;
import edu.midir.demo.pojo.enumeration.CommonEnum;
import edu.midir.demo.pojo.enumeration.EnumError;
import edu.midir.demo.pojo.exception.CheckException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.ConnectException;
import java.sql.SQLException;

/**
 * 这样可以全局的管理项目的异常现象，避免的错误信息直接显示到页面的尴尬。
 * 前端特定场景复现比较困难情况下非常有用
 * <p>
 * 所以关键是runtimeException 打日志。常见的可以不打日志。
 *
 * @author yangzhi
 * @since 2018/2/8
 */
@ControllerAdvice
@ResponseBody
public class ControllerExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);

    /**
     * 500错误-空指针错误
     */
    @ExceptionHandler(NullPointerException.class)
    public ResultBean nullPointerExceptionHandler(NullPointerException ex) {
        return result(EnumError.NULL_POINTER_EXCEPTION, ex);
    }


//    /**
//     * 500错误-程序员sql编写错误
//     */
//    @ExceptionHandler(BadSqlGrammarException.class)
//    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//    public ResultBean sqlExceptionHandler(BadSqlGrammarException ex) {
//        return result(EnumError.BAD_SQL_GRAMMAR_EXCEPTION, ex);
//    }

    /**
     * 500错误-类型转换错误
     */
    @ExceptionHandler(ClassCastException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultBean classCastExceptionHandler(ClassCastException ex) {
        return result(EnumError.CLASS_CAST_EXCEPTION, ex);
    }

    /**
     * 500错误-文件读取错误
     */
    @ExceptionHandler(IOException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)

    public ResultBean iOExceptionHandler(IOException ex) {
        return result(EnumError.IO_EXCEPTION, ex);
    }

    /**
     * 500错误--服务器代码未正确上传，或者API过时
     */
    @ExceptionHandler(NoSuchMethodException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultBean noSuchMethodExceptionHandler(NoSuchMethodException ex) {
        return result(EnumError.NO_SUCH_METHOD_EXCEPTION, ex);
    }

    /**
     * 500错误--数组下标越界
     */
    @ExceptionHandler(IndexOutOfBoundsException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultBean indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {
        return result(EnumError.INDEX_OUT_OF_BOUNDS_EXCEPTION, ex);
    }

    /**
     * 500错误--网络连接
     */
    @ExceptionHandler(ConnectException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultBean connectException(ConnectException ex) {
        return result(EnumError.CONNECT_EXCEPTION, ex);
    }

    /**
     * 400错误--参数不符
     */
    @ExceptionHandler({TypeMismatchException.class, MissingServletRequestParameterException.class
            , HttpMessageNotReadableException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResultBean requestTypeMismatch(TypeMismatchException ex) {
        return result(EnumError.BAD_REQUEST);
    }


    /**
     * 405错误--请求方法有限制
     */
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public ResultBean request405(HttpRequestMethodNotSupportedException ex) {
        return result(EnumError.METHOD_NOT_ALLOWED);
    }

    /**
     * 406错误--请求头不正确
     */
    @ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
    @ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
    public ResultBean request406(HttpMediaTypeNotAcceptableException ex) {
        return result(EnumError.NOT_ACCEPTABLE);
    }

    /**
     * 500错误
     */
    @ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultBean server500(HttpServletRequest request, RuntimeException e) {
        return result(EnumError.INTERNAL_SERVER_ERROR, request, e);
    }

    /**
     * 500错误-SQLException错误-->根本不会抛出的一个异常
     */
    @ExceptionHandler(SQLException.class)
    public ResultBean sqlExceptionHandler(SQLException ex) {
        return result(EnumError.SQL_Exception, ex);
    }

    /**
     * servletException
     */
    @ExceptionHandler({ServletException.class})
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResultBean http404(ServletException ex) {
        return result(EnumError.NOT_FOUND_REQUEST, ex);
    }

    /**
     * 500错误-运行时异常
     */
    @ExceptionHandler(CheckException.class)
    @ResponseStatus(HttpStatus.OK)
    public ResultBean checkExceptionHandler(HttpServletRequest request, CheckException e) {

        return result(e.getCode(), e.getMessage(), request, e);
    }
    /**
     * 500错误-运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultBean runtimeExceptionHandler(HttpServletRequest request, RuntimeException e) {

        return result(EnumError.RUNTIME_EXCEPTION, request, e);
    }

    /**
     * 不符合上述ClassName的方法。
     *
     * @return resultBean
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultBean handleAllException(HttpServletRequest request, Exception e) {
        logger.error("系统异常：" + e.getMessage() + " 位置：" + request.getServletPath(), e);
        return result(EnumError.INTERNAL_SERVER_ERROR);
    }


    private ResultBean result(CommonEnum commonEnum, HttpServletRequest request, Exception e) {
        logger.error("系统异常：" + e.getMessage() + " 位置：" + request.getServletPath(), e);
        return  ResultBean.failFromEnum(commonEnum);
    }
    private ResultBean result(String code, String msg, HttpServletRequest request, Exception e) {
        logger.error("系统异常：" + e.getMessage() + " 位置：" + request.getServletPath(), e);
        return ResultBean.fail(code,msg);
    }

    private ResultBean result(CommonEnum commonEnum) {
        return  ResultBean.failFromEnum(commonEnum);
    }

    private ResultBean result(CommonEnum commonEnum, Exception e) {
        logger.error("系统异常：" + e.getMessage(), e);
        return ResultBean.failFromEnum(commonEnum);
    }
}
